package poset;

import java.util.ArrayList;
import java.util.TreeMap;
import java.util.TreeSet;

import spaces.ChargeableVectorNode;
import spaces.VectorNode;
import spaces.NodeSpace;
import spaces.SphericalSurfaceSpace;
import vectors.Vector;

public class SphericalPosetSpace extends PosetSpace{
	
	private static final double FIX_FACTOR = 0.002;
	private static final double BUFFER = 0.1;
	private final int dof;
	
	public SphericalPosetSpace(String relation, int dof){
		this(relation, new ArrayList<User>(), dof);
	}
	
	public SphericalPosetSpace(String relation, ArrayList<User> users, int dof){
		super(relation,users);
		this.dof = dof;
	}
	/*
	// vectors from given user to each item that is in their poset
	private TreeMap<SphericalItem,Vector> getDifferences(SphericalUser user) {
		TreeMap<SphericalItem,Vector> diffs = new TreeMap<SphericalItem,Vector>();
		Vector userPos = user.getPos();
		for(SphericalItem item : user.getPoset(relation).getItems()){
			diffs.put(item, item.getPos().minus(userPos));
		}
		return diffs;
	}*/
	
	private TreeMap<Item,Double> getArcLengths(User user) {
		TreeMap<Item,Double> coses = new TreeMap<Item,Double>();
		for(Item item : user.getPoset(relation).getItems()){
			double cos = item.getPos().dot(user.getPos());
			if(cos>1){
				cos = 1;
			} else if(cos<-1){
				cos = -1;
			}
			coses.put(item, Math.acos(cos));
		}
		return coses;
	}
	

	public String getRelation(){
		return relation;
	}

	public int getDOF() {
		return dof;
	}

	public SphericalSurfaceSpace getSphericalSurfaceSpace() {
		if(!isInitialized){
			throw new IllegalStateException("Cannot call getSphericalSurfaceSpace until initialized.");
		}
		return (SphericalSurfaceSpace)space;
	}

	public double getTension() {
		return tension;
	}

	@Override
	protected NodeSpace createSpace(TreeSet<VectorNode> nodes) {
		return new SphericalSurfaceSpace(dof, nodes);
	}

	@Override
	protected TreeMap<Item, Vector> getDifferences(User user) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public double getBuffer() {
		return BUFFER;
	}

	@Override
	protected void iterate(User user) {
		TreeMap<Item,Double> arcs = getArcLengths(user);
		
		// Create Poset from distances
		Poset realRelOrder;
		realRelOrder = createPosetFromDistances(arcs);
		
		// Find User Orderings that are not present in the space
		ArrayList<Ordering> wrongOrders = user.getPoset(relation).getUnsatisfiedExplicitOrderings(realRelOrder);
		for(Ordering wo : wrongOrders){
			// Find amount incorrect
			double toLeftItem = arcs.get(wo.getItem1());
			double toRightItem = arcs.get(wo.getItem2());
			double error = toLeftItem - toRightItem + BUFFER;
			if(error<0){
				System.out.println(wo.getItem1()+"::"+toLeftItem);
				System.out.println(wo.getItem2()+"::"+toRightItem);
				System.out.println(BUFFER);
				System.err.println("This shouldn't happen in SphericalPosetSpace.next(), unexplainable poset");
				System.err.println("User said: "+user.getPoset(relation));
				System.err.println("World said: "+realRelOrder);
				System.exit(-1);
			}
			tension+=error;
			
			// Add charges to correct
			//double angleFactor1 = -Math.sqrt(toLeftItem);
			//double angleFactor2 = -Math.sqrt(toRightItem);
			//Vector uError1 = user.getPos().times(angleFactor1);
			//Vector iError1 = wo.getItem1().getPos().times(angleFactor1);
			//Vector uError2 = user.getPos().times(angleFactor2);
			//Vector iError2 = wo.getItem2().getPos().times(angleFactor2);
			Vector uPos = user.getPos();
			Vector iPos1 = wo.getItem1().getPos();
			Vector iPos2 = wo.getItem2().getPos();
			double cos1 = uPos.dot(iPos1);
			double cos2 = uPos.dot(iPos2);
			Vector uError1 = iPos1.divide(cos1).minus(uPos).normalize().times(FIX_FACTOR*toLeftItem);
			Vector iError1 = uPos.divide(cos1).minus(iPos1).normalize().times(FIX_FACTOR*toLeftItem);
			Vector uError2 = iPos2.divide(cos2).minus(uPos).normalize().times(-FIX_FACTOR*toRightItem);
			Vector iError2 = uPos.divide(cos2).minus(iPos2).normalize().times(-FIX_FACTOR*toRightItem);
			
			//Vector uError1 = uPos.minus(iPos1.times(cos1)).normalize().times(FIX_FACTOR).divide(cos1);
			//Vector iError1 = iPos1.minus(uPos.times(cos1)).normalize().times(FIX_FACTOR).divide(cos1);
			//Vector uError2 = uPos.minus(iPos2.times(cos2)).normalize().times(FIX_FACTOR).divide(cos2);
			//Vector iError2 = iPos2.minus(uPos.times(cos2)).normalize().times(FIX_FACTOR).divide(cos2);

			// flip errors that are facing wrong direction
			
			if(cos1 < 0){
				uError1 = uError1.times(-1);
			}
			if(cos1 < 0){
				iError1 = iError1.times(-1);
			}
			if(cos2 < 0){
				uError2 = uError2.times(-1);
			}
			if(cos2 < 0){
				iError2 = iError2.times(-1);
			}
			//uError1 = uError1.times(FIX_FACTOR);
			//uError2 = uError2.times(FIX_FACTOR);
			//iError1 = iError1.times(FIX_FACTOR);
			//iError2 = iError2.times(FIX_FACTOR);
			
			user.addCharge(uError1);
			user.addCharge(uError2);
			wo.getItem1().addCharge(iError1);
			wo.getItem2().addCharge(iError2);
		}
		
		
		//Apply the charges
		for(VectorNode node : space.getNodes()){
			((ChargeableVectorNode)node).applyCharge();
			node.setPos(node.getPos().normalize());
		}
	}

	@Override
	protected Vector randomInitialPosition() {
		return Vector.random(dof+1).times(2).plus(-1).normalize();
	}
}
